/*
    This file is part of Lua Invoke.
    Copyright 2005-2007 Rafael de Oliveira Jannone <rafael@jannone.org>

    Lua Invoke is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* marshalling from/to lua stack */

#include "marshall.h"
#include "callback.h"

/* marshalling */

long*
marshall(lua_State *L, int index, long* out, type_t* t) 
{
	switch (t->type) {
		case '!':
			{
				if (lua_isuserdata(L, index)) {
					callback_t* cb = *(callback_t**)lua_touserdata(L, index);
					*out = (long)(cb->callback);
					++out;
				} else
				if (lua_istable(L, index)) {
					lua_pushstring(L, "sym");
					lua_gettable(L, index);
					if (lua_islightuserdata(L, -1)) {
						*(long*)(out) = (long)lua_touserdata(L, -1);
						lua_pop(L, 1);
						++out;
					} else {
						// FIXME: report error/cleanup (goto?)
					}
				} else {
					// FIXME: report error/cleanup (goto?)
				}
			}
			break;
		case 'c':
			if (t->unsign)
			{
				*(unsigned char*)(out) = (unsigned char)lua_tonumber(L, index);
				++out;
			} else {
				*(char*)(out) = (char)lua_tonumber(L, index);
				++out;
			}
			break;

		case 'z':
			if (t->unsign)
			{
				*(unsigned short*)(out) = (unsigned short)lua_tonumber(L, index);
				++out;
			} else {
				*(short*)(out) = (short)lua_tonumber(L, index);
				++out;
			}
			break;

		case 'i':
			if (t->unsign)
			{
				*(unsigned int*)(out) = (unsigned int)lua_tonumber(L, index);
				++out;
			} else {
				*(int*)(out) = (int)lua_tonumber(L, index);
				++out;
			}
			break;

		case 'l':
			if (t->unsign)
			{
				*(unsigned long*)(out) = (unsigned long)lua_tonumber(L, index);
				++out;
			} else {
				*(long*)(out) = (long)lua_tonumber(L, index);
				++out;
			}
			break;

		case 'L':
			if (t->unsign)
			{
				*(unsigned long long*)(out) = (unsigned long long)lua_tonumber(L, index);
				out += 2;
			} else {
				*(long long*)(out) = (long long)lua_tonumber(L, index);
				out += 2;
			}
			break;
		
		case 'f':
			{
				*(float*)(out) = (float)lua_tonumber(L, index);
				++out;
			}
			break;
		
		case 's':
			{
				*(const char**)(out) = lua_tostring(L, index);
				++out;
			}
			break;
		
		case 'd':
			{
				*(double*)(out) = (double)lua_tonumber(L, index);
				out += 2;
			}
			break;
		case 'p':			
		case 'P':
			{
				if (lua_islightuserdata(L, index)) {
					*(long*)(out) = (long)lua_touserdata(L, index);
					++out;
				} else
				if (lua_istable(L, index)) {
					int nindex;
					lua_pushstring(L, "handle");
					lua_gettable(L, index);
					nindex = lua_gettop(L);
					out = marshall(L, nindex, out, t);
					lua_pop(L, 1);
				} else {
					long* p = (long*)lua_touserdata(L, index);
					if (p) {
						*(long*)out = *(long*)p;
					} else {
						*(long*)out = (long)p;
					}
					++out;
				}
			}
			break;
	}
	return out;
}

int
demarshall(lua_State* L, LL ret, type_t* t)
{
	/* demarshall return value, FIXME: push byref values as well */
	switch (t->type) {
		case 'c':	/* char */
			if (t->unsign)
			{
				unsigned char v = (unsigned char)ret;
				lua_pushnumber(L, v);
			} else {
				char v = (char)ret;
				lua_pushnumber(L, v);
			}
			break;
		case 'z':	/* short */
			if (t->unsign)
			{
				unsigned short v = (unsigned short)ret;
				lua_pushnumber(L, v);
			} else {
				short v = (short)ret;
				lua_pushnumber(L, v);
			}
			break;
		case 'i':	/* integer */
			if (t->unsign)
			{
				unsigned int v = (unsigned int)ret;
				lua_pushnumber(L, v);
			} else {
				int v = (int)ret;
				lua_pushnumber(L, v);
			}
			break;		
		case 'l':	/* long */
			if (t->unsign)
			{
				unsigned long v = (unsigned long)ret;
				lua_pushnumber(L, v);
			} else {
				long v = (long)ret;
				lua_pushnumber(L, v);
			}
			break;		
		case 'f':	/* float */
			{
				float v = (float)ret;
				lua_pushnumber(L, v);
			}
			break;
		case 's':	/* string (const char*) */
			{
				const char* v = *(const char**)&ret;
				lua_pushstring(L, v);
			}
			break;		
		case 'L':	/* long long */
			if (t->unsign)
			{
				unsigned long long v = (unsigned long long)ret;
				lua_pushnumber(L, v);
			} else {
				long long v = (long long)ret;
				lua_pushnumber(L, v);
			}
			break;		
		case 'd':	/* double */
			{
				double v = *(double*)&ret;
				lua_pushnumber(L, v);
			}
			break;
		case 'p':	/* pointer */
			{
				long* v = *(long**)&ret;
				lua_pushlightuserdata(L, v);
			}
			break;
		case 'P':	/* heavy-pointer (userdata) */
			{
				long* addr = (long*)lua_newuserdata (L, sizeof(long));
				long v = *(long*)&ret;
				*addr = v;
			}
			break;
		default:
			return 0;
	}
	return 1;
}
